// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //
//  »Project«   Talina Gaming System (TgS) (∂)
//  »File«      TgS Common - Math API [Vector] [F] [F].i_inc
//  »Author«    Andrew Aye (EMail: mailto:andrew.aye@gmail.com, Web: http://www.andrewaye.com)
//  »Version«   4.0
// ------------------------------------------------------------------------------------------------------------------------------ //
//  Copyright: © 2002-2010, Andrew Aye.  All Rights Reserved.
//  This software is free for non-commercial use. Redistribution and use in source and binary forms, with or without modification,
//  are permitted provided that the following conditions are met: 
//    Redistributions of source code must retain this copyright notice, this list of conditions and the following disclaimers. 
//    Redistributions in binary form must reproduce this copyright notice, this list of conditions and the following
//      disclaimers in the documentation and other materials provided with the distribution. 
//  Neither the names of the copyright owner nor the names of its contributors may be used to endorse or promote products derived
//  from this software without specific prior written permission. 
//  The intellectual property rights of the algorithms used reside with Andrew Aye.  You may not use this software, in whole or
//  in part, in support of any commercial product without the express written consent of the author.
//  There is no warranty or other guarantee of fitness of this software for any purpose. It is provided solely "as is".
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //

// ---- SCALAR - ARITHMETIC OPERATIONS ------------------------------------------------------------------------------------------ //

TgINLINE V(TgVEC) V(F_ADD_VV)( V(CPCU_TgVEC) ptv0, V(CPCU_TgVEC) ptv1 )
{
    #define EQN(A) (ptv0->m_aData[A] + ptv1->m_aData[A])
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_MAD_VVV)( V(CPCU_TgVEC) ptv0, V(CPCU_TgVEC) ptv1, V(CPCU_TgVEC) ptv2 )
{
    #define EQN(A) ptv0->m_aData[A]*ptv1->m_aData[A] + ptv2->m_aData[A]
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_NMS_VVV)( V(CPCU_TgVEC) ptv0, V(CPCU_TgVEC) ptv1, V(CPCU_TgVEC) ptv2 )
{
    #define EQN(A) -(ptv0->m_aData[A]*ptv1->m_aData[A] - ptv2->m_aData[A])
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE TYPE V(F_DOT_VV)( V(CPCU_TgVEC) ptv0, V(CPCU_TgVEC) ptv1 )
{
    return (
          ptv0->m_aData[ 0] * ptv1->m_aData[ 0]
    #if (DIM > 1)
        + ptv0->m_aData[ 1] * ptv1->m_aData[ 1]
    #endif
    #if (DIM > 2)
        + ptv0->m_aData[ 2] * ptv1->m_aData[ 2]
    #endif
    #if (DIM > 3)
        + ptv0->m_aData[ 3] * ptv1->m_aData[ 3]
    #endif
    #if (DIM > 4)
        + ptv0->m_aData[ 4] * ptv1->m_aData[ 4]
    #endif
    #if (DIM > 5)
        + ptv0->m_aData[ 5] * ptv1->m_aData[ 5]
    #endif
    #if (DIM > 6)
        + ptv0->m_aData[ 6] * ptv1->m_aData[ 6]
    #endif
    #if (DIM > 7)
        + ptv0->m_aData[ 7] * ptv1->m_aData[ 7]
    #endif
    #if (DIM > 8)
        + ptv0->m_aData[ 8] * ptv1->m_aData[ 8]
    #endif
    #if (DIM > 9)
        + ptv0->m_aData[ 9] * ptv1->m_aData[ 9]
    #endif
    #if (DIM > 10)
        + ptv0->m_aData[10] * ptv1->m_aData[10]
    #endif
    #if (DIM > 11)
        + ptv0->m_aData[11] * ptv1->m_aData[11]
    #endif
    #if (DIM > 12)
        + ptv0->m_aData[12] * ptv1->m_aData[12]
    #endif
    #if (DIM > 13)
        + ptv0->m_aData[13] * ptv1->m_aData[13]
    #endif
    #if (DIM > 14)
        + ptv0->m_aData[14] * ptv1->m_aData[14]
    #endif
    #if (DIM > 15)
        + ptv0->m_aData[15] * ptv1->m_aData[15]
    #endif
    );
}


TgINLINE V(TgVEC) V(F_MUL_VV)( V(CPCU_TgVEC) ptv0, V(CPCU_TgVEC) ptv1 )
{
    #define EQN(A) ptv0->m_aData[A] * ptv1->m_aData[A]
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_DIV_VV)( V(CPCU_TgVEC) ptv0, V(CPCU_TgVEC) ptv1 )
{
    #define EQN(A) ptv0->m_aData[A] / ptv1->m_aData[A]
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_SUB_VV)( V(CPCU_TgVEC) ptv0, V(CPCU_TgVEC) ptv1 )
{
    #define EQN(A) ptv0->m_aData[A] - ptv1->m_aData[A]
    VEC_ASSIGN_EQN;
    #undef EQN
}




// ---- SCALAR - CALCULATIONS --------------------------------------------------------------------------------------------------- //

TgINLINE TYPE V(F_LSQ)( V(CPCU_TgVEC) ptv0 )
{
    return (V(F_DOT_VV)( ptv0, ptv0 ));
}


TgINLINE TYPE V(F_LEN)( V(CPCU_TgVEC) ptv0 )
{
    return (T(tgPM_SQRT)( V(F_LSQ)( ptv0 ) ));
}


TgINLINE V(TgVEC) V(F_NEG)( V(CPCU_TgVEC) ptv0 )
{
    #define EQN(A) ptv0->m_aData[A] * MKL(-1.0)
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_SQRT)( V(CPCU_TgVEC) ptv0 )
{
    #define EQN(A) T(tgPM_SQRT)( ptv0->m_aData[A] )
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_RSQRT)( V(CPCU_TgVEC) ptv0 )
{
    #define EQN(A) MKL(1.0) / T(tgPM_SQRT)( ptv0->m_aData[A] )
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_NORM)( V(CPCU_TgVEC) ptv0 )
{
    TYPE                            tyLength;

    return (V(F_NORM_LEN)( &tyLength, ptv0 ));
}


#if (DIM==3)
    TgINLINE V(TgVEC) V(F_NORM_LEN)( TYPE * __restrict const ptyLength, V(CPCU_TgVEC) ptvS0 )
    {
        const TYPE                          tya0 = T(tgPM_ABS)(ptvS0->m_aData[0]);
        const TYPE                          tya1 = T(tgPM_ABS)(ptvS0->m_aData[1]);
        const TYPE                          tya2 = T(tgPM_ABS)(ptvS0->m_aData[2]);
        const TYPE                          tyM0 = T(tgCM_MAX)( tya0, tya1 );
        const TYPE                          tyM1 = T(tgCM_MAX)( tya1, tya2 );
        const TYPE                          tyMX = T(tgCM_MAX)( tyM0, tyM1 );
        const TYPE                          ty0 = ptvS0->m_aData[0] / tyMX;
        const TYPE                          ty1 = ptvS0->m_aData[1] / tyMX;
        const TYPE                          ty2 = ptvS0->m_aData[2] / tyMX;
        const TYPE                          tyLength = T(tgPM_SQRT)( ty0*ty0 + ty1*ty1 + ty2*ty2 );
        const TYPE                          tyInvLength = MKL(1.0) / tyLength;

        *ptyLength = tyLength*tyMX;

        return (V(F_SET_ELEM)( ty0*tyInvLength, ty1*tyInvLength, ty2*tyInvLength ));
    }
#elif (DIM==4)
    TgINLINE V(TgVEC) V(F_NORM_LEN)( TYPE * __restrict const ptyLength, V(CPCU_TgVEC) ptvS0 )
    {
        const TYPE                          tya0 = T(tgPM_ABS)(ptvS0->m_aData[0]);
        const TYPE                          tya1 = T(tgPM_ABS)(ptvS0->m_aData[1]);
        const TYPE                          tya2 = T(tgPM_ABS)(ptvS0->m_aData[2]);
        const TYPE                          tya3 = T(tgPM_ABS)(ptvS0->m_aData[3]);
        const TYPE                          tyM0 = T(tgCM_MAX)( tya0, tya1 );
        const TYPE                          tyM1 = T(tgCM_MAX)( tya2, tya3 );
        const TYPE                          tyMX = T(tgCM_MAX)( tyM0, tyM1 );
        const TYPE                          ty0 = ptvS0->m_aData[0] / tyMX;
        const TYPE                          ty1 = ptvS0->m_aData[1] / tyMX;
        const TYPE                          ty2 = ptvS0->m_aData[2] / tyMX;
        const TYPE                          ty3 = ptvS0->m_aData[3] / tyMX;
        const TYPE                          tyLength = T(tgPM_SQRT)( ty0*ty0 + ty1*ty1 + ty2*ty2 + ty3*ty3 );
        const TYPE                          tyInvLength = MKL(1.0) / tyLength;

        *ptyLength = tyLength*tyMX;

        return (V(F_SET_ELEM)( ty0*tyInvLength, ty1*tyInvLength, ty2*tyInvLength, ty3*tyInvLength ));
    }
#else
    TgINLINE V(TgVEC) V(F_NORM_LEN)( TYPE * __restrict const pfLength, V(CPCU_TgVEC) ptv0 )
    {
        return (V(F_DIV_VS)( ptv0, V(F_LEN)( ptv0 ) ));
    }
#endif




// ---- SCALAR - BOUNDS --------------------------------------------------------------------------------------------------------- //

TgINLINE V(TgVEC) V(F_FLR)( V(CPCU_TgVEC) ptv0 )
{
    #define EQN(A) T(tgPM_FLOOR)(ptv0->m_aData[A])
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_CEL)( V(CPCU_TgVEC) ptv0 )
{
    #define EQN(A) T(tgPM_CEIL)(ptv0->m_aData[A])
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_CLP)( V(CPCU_TgVEC) ptv0, V(CPCU_TgVEC) ptvMin, V(CPCU_TgVEC) ptvMax )
{
    #define EQN(A) T(tgCM_CLP)(ptv0->m_aData[A], ptvMin->m_aData[A], ptvMax->m_aData[A])
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_SAT)( V(CPCU_TgVEC) ptv0 )
{
    #define EQN(A) T(tgCM_CLP)(ptv0->m_aData[A], MKL(0.0), MKL(1.0))
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE TgBOOL V(F_NaN)( V(CPCU_TgVEC) ptv0 )
{
    TgUINT32                            uiIndex = 0;

    for (uiIndex = 0; uiIndex < DIM; ++uiIndex)
    {
        if (T(tgCM_NaN)( ptv0->m_aData[uiIndex] ))
        {
            return (TgTRUE);
        };
    };

    return (TgFALSE);
}


TgINLINE TgBOOL V(F_BND)( V(CPCU_TgVEC) ptv0, V(CPCU_TgVEC) ptvBound )
{
    TgUINT32                            uiIndex = 0;

    for (uiIndex = 0; uiIndex < DIM; ++uiIndex)
    {
        if ((-ptvBound->m_aData[uiIndex] > ptv0->m_aData[uiIndex]) || (ptv0->m_aData[uiIndex] > ptvBound->m_aData[uiIndex]))
        {
            return (TgFALSE);
        };
    };

    return (TgTRUE);
}




// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
//  Scalar Function
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //

// ---- SCALAR - SCALAR ARITHMETIC OPERATIONS ----------------------------------------------------------------------------------- //

TgINLINE V(TgVEC) V(F_MAD_SVV)( const TYPE tyScalar, V(CPCU_TgVEC) ptv1, V(CPCU_TgVEC) ptv2 )
{
    #define EQN(A) tyScalar*ptv1->m_aData[A] + ptv2->m_aData[A]
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_MAD_VSV)( V(CPCU_TgVEC) ptv0, const TYPE tyScalar, V(CPCU_TgVEC) ptv2 )
{
    #define EQN(A) ptv0->m_aData[A]*tyScalar + ptv2->m_aData[A]
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_NMS_SVV)( const TYPE tyScalar, V(CPCU_TgVEC) ptv1, V(CPCU_TgVEC) ptv2 )
{
    #define EQN(A) -(tyScalar*ptv1->m_aData[A] - ptv2->m_aData[A])
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_NMS_VSV)( V(CPCU_TgVEC) ptv0, const TYPE tyScalar, V(CPCU_TgVEC) ptv2 )
{
    #define EQN(A) -(ptv0->m_aData[A]*tyScalar - ptv2->m_aData[A])
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_MUL_SV)( const TYPE tyScalar, V(CPCU_TgVEC) ptv1 )
{
    #define EQN(A) tyScalar * ptv1->m_aData[A]
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_MUL_VS)( V(CPCU_TgVEC) ptv0, const TYPE tyScalar )
{
    #define EQN(A) ptv0->m_aData[A] * tyScalar
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_DIV_SV)( const TYPE tyScalar, V(CPCU_TgVEC) ptv1 )
{
    #define EQN(A) tyScalar / ptv1->m_aData[A]
    VEC_ASSIGN_EQN;
    #undef EQN
}


TgINLINE V(TgVEC) V(F_DIV_VS)( V(CPCU_TgVEC) ptv0, const TYPE tyScalar )
{
    const TYPE tyInvScalar = MKL(1.0) / tyScalar;
    #define EQN(A) ptv0->m_aData[A] * tyInvScalar
    VEC_ASSIGN_EQN;
    #undef EQN
}